from django.shortcuts import render
from django.views import View
import json, pickle, base64
from django import http
from django_redis import get_redis_connection

from goods.models import SKU
import logging
from meiduo_mall.utils.response_code import RETCODE

logger = logging.getLogger('django')


class CartsView(View):

    def post(self, request):

        # 1.接收请求体json
        json_dict = json.loads(request.body.decode())
        sku_id = json_dict.get('sku_id')
        count = json_dict.get('count')
        selected = json_dict.get('selected', True)
        # 2.校验
        if all([sku_id, count]) is False:
            return http.HttpResponseForbidden('缺少必传参数')
        try:
            sku = SKU.objects.get(id=sku_id, is_launched=True)
        except SKU.DoesNotExist:
            return http.HttpResponseForbidden('sku_id有误')
        try:
            count = int(count)
        except Exception as e:
            logger.error('count类型有误')
            return http.HttpResponseForbidden('count类型有误')

        if isinstance(selected, bool) is False:
            return http.HttpResponseForbidden('selected类型有误')
        # 创建响应对象
        response = http.JsonResponse({'code': RETCODE.OK, 'errmsg': '添加购物车成功'})
        # 3. 判断用户是否登录
        user = request.user
        if user.is_authenticated:
            # 登录用户存储购物车数据到redis
            """
            hash: {sku_id_1: 1, sku_id_2: 2}
            set: {sku_id_1}
            """
            # 创建redis连接对象
            redis_cli = get_redis_connection('carts')
            # 创建管道对象
            pl = redis_cli.pipeline()
            # 将sku_id和count 向hash里面存储
            # redis_cli.hincrby(key=对象, 对象属性名, 属性值)
            pl.hincrby('cart_%s' % user.id, sku_id, count)  # hincrby添加sku_id时如果已存在,就会把原count和本次count累加再存储
            # 判断当前商品是否勾选,如果勾选就将sku_id添加到set中,如果没勾选什么也不做
            if selected:
                pl.sadd('selected_%s' % user.id, sku_id)
            pl.execute()  # 执行管道

        else:
            # 1. 未登录用户存储购物车数据cookie
            """
            {
                sku_id_15: {'count': 1, 'selected': True} ,
                sku_id_2: {'count': 2, 'selected': False} ,
            }
            """
            # 2. 尝试先获取cookie购物车数据
            cart_str = request.COOKIES.get('carts')
            # 3. 判断是否有cookie购物车数据
            if cart_str:
                # 4. 如果cookie已经有购物车数据, 将cart_str ---> dict
                cart_str_bytes = cart_str.encode()  # str ---> b'str'
                cart_unicode_bytes = base64.b64decode(cart_str_bytes)  # b'str' ---> b'unicode'
                cart_dict = pickle.loads(cart_unicode_bytes)  # b'unicode' ---> dict
                # 5. 判断当前要添加的sku_id在cart_dict中是否存在, 如果存在,就把原有count和本次count累加再赋值进去
                if sku_id in cart_dict:
                    origin_count = cart_dict[sku_id]['count']
                    count += origin_count  # count = count + origin_count

            else:
                # 6. 如果cookie中没有购物车数据, 就定义一个空的用来包装购物车大字典
                cart_dict = {}

            # 7. 包装购物车dict数据
            cart_dict[sku_id] = {'count': count, 'selected': selected}  # 新增or修改
            # 将cart_dict ---> cart_str
            cart_unicode_bytes = pickle.dumps(cart_dict)  # dict  --->  b'unicode'
            cart_str_bytes = base64.b64encode(cart_unicode_bytes)  # b'unicode'  --> b'str'
            cart_str = cart_str_bytes.decode()  # b'str' --> str

            # 存储cookie
            response.set_cookie('carts', cart_str, max_age=None)
        # 响应
        return response

    def get(self, request):
        """购物车展示"""
        # 判断用户是否登录
        user = request.user
        if user.is_authenticated:
            # 登录用户从redis获取购物车数据
            redis_cli = get_redis_connection('carts')
            redis_dict = redis_cli.hgetall('cart_%s' % user.id)
            if not redis_dict:  # 如果用户还没有购物车获取hash返回 {}
                return render(request, 'cart.html')
            selected_ids = redis_cli.smembers('selected_%s' % user.id)
            # 将redis购物车数据格式转换成和cookie一样的大字典格式,目的:查询及包装模板需要的数据代码共享
            # 定义一个字典用来包装购物车数据
            cart_dict = {}
            for sku_id_bytes in redis_dict:
                cart_dict[int(sku_id_bytes)] = {
                    'count': int(redis_dict[sku_id_bytes]),
                    'selected': sku_id_bytes in selected_ids
                }
        else:
            # 未登录用户从cookie中获取购物车数据
            cart_str = request.COOKIES.get('carts')
            if cart_str:
                # 获取cookie购物车数据,并从str ---> dict
                cart_dict = pickle.loads(base64.b64decode(cart_str.encode()))
            else:
                # 如果用户还没有购物车数据,展示空白的购物车界面
                return render(request, 'cart.html')

        # 查询购物车中所有sku_id对应的sku模型
        sku_qs = SKU.objects.filter(id__in=cart_dict.keys())
        # 定义一个列表用来包装所有购物车商品字典
        sku_list = []
        for sku in sku_qs:
            selected = cart_dict[sku.id]['selected']
            count = cart_dict[sku.id]['count']
            sku_list.append({
                # 注册前面bool和 Decimal类型问题注意车换成str
                'id': sku.id,
                'name': sku.name,
                'selected': str(selected),  # True, False  true, false, YES, NO
                'price': str(sku.price),  # Decimal
                'default_image_url': sku.default_image.url,
                'amount': str(sku.price * count),
                'count': count
            })

        # 包装
        context = {'cart_skus': sku_list}

        return render(request, 'cart.html', context)

    def put(self, request):
        """购物车修改"""
        # 1.接收请求体json
        json_dict = json.loads(request.body.decode())
        sku_id = json_dict.get('sku_id')
        count = json_dict.get('count')
        selected = json_dict.get('selected', True)
        # 2.校验
        if all([sku_id, count]) is False:
            return http.HttpResponseForbidden('缺少必传参数')
        try:
            sku = SKU.objects.get(id=sku_id, is_launched=True)
        except SKU.DoesNotExist:
            return http.HttpResponseForbidden('sku_id有误')
        try:
            count = int(count)
        except Exception as e:
            logger.error('count类型有误')
            return http.HttpResponseForbidden('count类型有误')

        if isinstance(selected, bool) is False:
            return http.HttpResponseForbidden('selected类型有误')

        cart_sku = {
            # 注册前面bool和 Decimal类型问题注意车换成str
            'id': sku.id,
            'name': sku.name,
            'selected': selected,  # True, False  true, false, YES, NO
            'price': sku.price,  # Decimal
            'default_image_url': sku.default_image.url,
            'amount': sku.price * count,
            'count': count
        }
        # 创建响应对象
        response = http.JsonResponse({'code': RETCODE.OK, 'errmsg': '修改购物车成功', 'cart_sku': cart_sku})

        # 判断用户是否登录
        user = request.user
        if user.is_authenticated:
            # 登录用户修改redis购物车数据
            # 创建redis连接对象
            redis_cli = get_redis_connection('carts')
            # 创建管道对象
            pl = redis_cli.pipeline()
            # 修改hash中商品count
            pl.hset('cart_%s' % user.id, sku_id, count)

            if selected:
                # 如果勾选,就将当前sku_id添加到set中
                pl.sadd('selected_%s' % user.id, sku_id)
            else:
                # 不勾选,将sku_id 从set中移除
                pl.srem('selected_%s' % user.id, sku_id)
            # 执行管道
            pl.execute()

        else:
            # 未登录用户修改cookie购物车数据
            # 获取cookie购物车数据
            cart_str = request.COOKIES.get('carts')
            # 判断是否有cookie购物车数据
            if cart_str:
                # 如果有 将cart_str ---> cart_dict
                cart_dict = pickle.loads(base64.b64decode(cart_str.encode()))
            else:
                # 如果没有获取到cookie购物车数据, 提前响应
                return http.HttpResponseForbidden('没有cookie购物车数据')

            # 直接用新数据覆盖旧数据
            cart_dict[sku_id] = {'count': count, 'selected': selected}
            # 将cart_dict ---> cart_str
            cart_str = base64.b64encode(pickle.dumps(cart_dict)).decode()

            response.set_cookie('carts', cart_str)
        # 响应
        return response

    def delete(self, request):
        # 接收请求体json
        json_dict = json.loads(request.body.decode())
        sku_id = json_dict.get('sku_id')
        # 校验
        try:
            sku = SKU.objects.get(id=sku_id)
        except SKU.DoesNotExist:
            return http.HttpResponseForbidden('sku_id有误')

        # 创建共用的响应对象
        response = http.JsonResponse({'code': RETCODE.OK, 'errmsg': '删除购物车成功'})
        # 判断用户是否登录
        user = request.user
        if user.is_authenticated:
            # 登录用户操作redis购物车数据
            # 创建redis连接对象
            redis_cli = get_redis_connection('carts')
            pl = redis_cli.pipeline()
            # 删除hash中指定属性和值
            pl.hdel('cart_%s' % user.id, sku_id)
            # 将sku_id 从set中移除
            pl.srem('selected_%s' % user.id, sku_id)

            pl.execute()

        else:
            # 未登录用户操作cookie购物车数据
            # 获取cookie购物车数据
            cart_str = request.COOKIES.get('carts')
            # 判断是否有cookie购物车数据
            if cart_str:
                # 如果有,将cart_str ---> cart_dict
                cart_dict = pickle.loads(base64.b64decode(cart_str.encode()))
            else:
                # 如果没有,提前响应
                return http.HttpResponseForbidden('缺少cookie购物车数据')
            # 删除前先判断要删除的key 在字典中是否存在,存在再去删除
            if sku_id in cart_dict:
                # 删除cart_dict 中指定键值对
                del cart_dict[sku_id]

            # 如果cookie购物车字典已经删空
            if not cart_dict:  # {}
                response.delete_cookie('carts')
                return response

            # cart_dict  --> cart_str
            cart_str = base64.b64encode(pickle.dumps(cart_dict)).decode()
            # 设置cookie
            response.set_cookie('carts', cart_str)

        return response


class CartsSelectedAllView(View):
    """购物车全选"""
    def put(self, request):
        # 接收请求json
        json_dict = json.loads(request.body.decode())
        selected = json_dict.get('selected')
        # 校验
        if isinstance(selected, bool) is False:
            return http.HttpResponseForbidden('参数类型有误')

        # 提前创建共用的响应对象
        response = http.JsonResponse({'code': RETCODE.OK, 'errmsg': '购物车全选成功'})
        # 判断用户是否登录
        user = request.user
        if user.is_authenticated:
            # 登录用户操作redis购物车数据
            # 创建redis连接对象
            redis_cli = get_redis_connection('carts')
            # 判断要全选还是取消全选
            if selected:
                # 如果是全选
                # 将hash数据全部取到
                redis_dict = redis_cli.hgetall('cart_%s' % user.id)
                # 将hash中的所有sku_id 添加到set中
                redis_cli.sadd('selected_%s' % user.id, *redis_dict.keys())
                # redis_cli.sadd('selected_%s' % user.id, *[1, 2])
            else:
                # 取消全部,将set删除
                redis_cli.delete('selected_%s' % user.id)

        else:
            # 未登录用户操作cookie购物车
            # 获取cookie购物车数据
            cart_str = request.COOKIES.get('carts')
            # 判断是否有购物车数据
            if cart_str:
                # 如果有,将cart_str  ---> cart_dict
                cart_dict = pickle.loads(base64.b64decode(cart_str.encode()))
            else:
                # 如果没有,提前响应
                return http.HttpResponseForbidden('没有购物车数据')
            # 遍历cart_dict 将内部的每个小字典selected值修改掉
            for sku_dict in cart_dict.values():
                sku_dict['selected'] = selected
            # cart_dict  ---> cart_str
            cart_str = base64.b64encode(pickle.dumps(cart_dict)).decode()
            # 设置cookie
            response.set_cookie('carts', cart_str)


        return response

"""
    hash: {sku_id_1: 1, sku_id_2: 2}
    set: {sku_id_1}
"""


class CartsSimpleView(View):
    """展示简单版购物车数据"""
    def get(self, request):
        # 判断用户是否登录
        user = request.user
        if user.is_authenticated:
            # 登录用户从redis获取购物车数据
            redis_cli = get_redis_connection('carts')
            redis_dict = redis_cli.hgetall('cart_%s' % user.id)
            if not redis_dict:  # 如果用户还没有购物车获取hash返回 {}
                return http.JsonResponse({'code': RETCODE.NODATAERR, 'errmsg': '没有购物车数据'})
            selected_ids = redis_cli.smembers('selected_%s' % user.id)
            # 将redis购物车数据格式转换成和cookie一样的大字典格式,目的:查询及包装模板需要的数据代码共享
            # 定义一个字典用来包装购物车数据
            cart_dict = {}
            for sku_id_bytes in redis_dict:
                cart_dict[int(sku_id_bytes)] = {
                    'count': int(redis_dict[sku_id_bytes]),
                    'selected': sku_id_bytes in selected_ids
                }
        else:
            # 未登录用户从cookie中获取购物车数据
            cart_str = request.COOKIES.get('carts')
            if cart_str:
                # 获取cookie购物车数据,并从str ---> dict
                cart_dict = pickle.loads(base64.b64decode(cart_str.encode()))
            else:
                # 如果用户还没有购物车数据,展示空白的购物车界面
                return http.JsonResponse({'code': RETCODE.NODATAERR, 'errmsg': '没有购物车数据'})

        # 查询购物车中所有sku_id对应的sku模型
        sku_qs = SKU.objects.filter(id__in=cart_dict.keys())
        # 定义一个列表用来包装所有购物车商品字典
        sku_list = []
        for sku in sku_qs:
            selected = cart_dict[sku.id]['selected']
            count = cart_dict[sku.id]['count']
            sku_list.append({
                # 注册前面bool和 Decimal类型问题注意车换成str
                'id': sku.id,
                'name': sku.name,
                'default_image_url': sku.default_image.url,
                'count': count
            })

        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK', 'cart_skus': sku_list})

"""
    {
       
        sku_id_1: {'count': 1, 'selected': True},
        sku_id_2: {'count': 1, 'selected': False}
    }
"""

